/*
 * otfeed_exchange_list.cpp This example program illustrates the mechanism
 * to be used for connecting to the otFeed servers and obtaining a list
 * of exchanges from the servers.
 */
#include "otfeed_servers.h"
#include <OTClient.h>
#include <sstream>
#include <iostream>
#include <iomanip>
 
namespace
{
    /**
     * Client class used to login and obtain the list of exchanges from 
     * otFeed servers. 
     */
    class ExchangeListClient : public OTClient
    {
    public:
        /**
         * Default constructor.
         */
        ExchangeListClient() ;
        
        /**
         * Return true if a list of exchanges has been received by the client.
         */
        bool
        IsExchangeListReceived() const ;
        
        /**
         * Return true if an error was encountered while communicating with
         * the otFeed servers.
         */
        bool 
        IsErrorEncountered() const ; 
                
        //
        // Overloaded OTClient methids. 
        //
        virtual void onLogin() ;
        virtual void onListExchanges( OTExchange* exchangeList, 
                                      int         numOfExchanges ) ;
        virtual void onError( OTError* otError ) ;                              
    private:    
        //! Flag that indicates if the exchange list has been received.
        bool mIsExchangeListReceived ;
        
        /**
         * Flag that indicates if an error has been encoutered while communicating
         * with the otFeed server.
         */
        bool mIsErrorEncountered ;
    } ;
}

int main( int    argc, 
          char** argv )
{
    /*
     * Read the username and password from the terminal.
     */
    std::string login ;
    std::string password ;
    //
    printf ("Please, enter your login: ");
    std::cin >> login ;
    //
    password = getpass( "Please enter your password: " ) ;
    //
    // Create an instance of our sample client and try to log in to
    // the server.
    //
    ExchangeListClient client ;
    for(int i=0; i<OtfeedUtilities::NUM_OF_OTFEED_SERVERS; ++i)
    {
        //
        // :KLUDGE: The const_cast<> is necessary below because OTClient::addHost
        // requires a 'char*' argument even though it should really be a 
        // 'const char*' or a 'const std::string'. Hopefully this will change
        // in future version of otFeed API and we will not need the const_cast<>
        // at that point.
        //
        client.addHost( const_cast<char*>( OtfeedUtilities::OTFEED_SERVERS[i].mAddress.c_str() ),
                        OtfeedUtilities::OTFEED_SERVERS[i].mPort ) ;
    }    
    //
    std::cout << "Logging in to the otFeed servers..." 
              << std::endl ; 
    //
    // :KLUDGE: OTClient::login() takes two 'char*' arguments in the
    // current version of OTFeed. However, it should really take
    // 'const char*' arguments. In order to use the current version of
    // OTClient::login() we have to use a const_cast<> below. Hope
    // that this will be fixed in future versions.
    //
    client.login( const_cast<char*>( login.c_str() ), 
                  const_cast<char*>( password.c_str() ) ) ;
    //
    // Wait for the client to request and receive the exchange list. 
    //
    while ( ( ! client.IsExchangeListReceived() ) &&
            ( ! client.IsErrorEncountered() ) )
    {
        sleep(1) ;        
    }          
    //
    if ( client.IsErrorEncountered() )
    {
        return EXIT_FAILURE ;    
    }       
    return EXIT_SUCCESS ;
} 

namespace
{
    inline
    ExchangeListClient::ExchangeListClient() : 
        mIsExchangeListReceived(false)
    {
        /* empty */    
    }
    
    inline
    bool
    ExchangeListClient::IsExchangeListReceived() const
    {
        return mIsExchangeListReceived ;    
    }
    
    inline
    bool 
    ExchangeListClient::IsErrorEncountered() const
    {
        return mIsErrorEncountered ;    
    }
    
    void
    ExchangeListClient::onLogin()
    {
        std::cout << "Login successful."
                  << std::endl ;
        std::cout << "Requesting list of exchanges ... " ;
        //
        // Request a list of exchanges that can be accessed by the current
        // user. The onListExchanges() method will be called when this is received
        // from the server. We ignore the request_id returned by this method
        // in this sample code.
        //
        const int request_id = this->requestListExchanges() ;        
        std::cout << "done." << std::endl ;
        //
        return ;
    }
    
    void
    ExchangeListClient::onListExchanges( OTExchange* const exchangeList, 
                                         const int         numOfExchanges )
    {
        std::cout << "List of exchanges received."
                  << std::endl ;
        //
        // Do not do anything if the exchangeList is NULL. 
        //
        if ( ( 0 == exchangeList ) ||
             ( 0 == numOfExchanges ) )
        {
            std::cerr << "WARNING: Exchange list is empty."
                      << std::endl ;
            return ;
        }
        //
		// Display a formatted list of exchanges. A lot of magic numbers are
		// used here to format the list. These are chosen fairly arbitrarily
		// and through trial and error. They have little or no effect on the
		// logic of the program.
		// 
        std::cout << "List of supported exchanges (total "
                  << numOfExchanges << "):" 
                  << std::endl ; 
        std::cout << std::setw(80)
                  << std::setfill('-')
                  << ""
                  << std::setfill(' ')
                  << std::endl ;
        std::cout << std::setw(8)
                  << std::left
                  << "Code"
                  << std::setw(60)
                  << std::left
                  << "Title"
                  << std::setw(10)
                  << "Available?"
                  << std::endl ;     
        std::cout << std::setw(80)
                  << std::setfill('-')
                  << ""
                  << std::setfill(' ')
                  << std::endl ;
        for(int i=0; i<numOfExchanges; ++i)
        {
            std::cout << std::setw(8)
                      << exchangeList[i].getCode()
                      << std::setw(60)
                      << std::left
                      << exchangeList[i].getTitle() 
                      << std::setw(10)
                      << ( ( exchangeList[i].getAvailable() ) ?
                           "Yes" : "No " ) 
                      << std::endl ;
        }   
        std::cout << std::setw(80)
                  << std::setfill('-')
                  << ""
                  << std::setfill(' ')
                  << std::endl ;                
        this->mIsExchangeListReceived = true ;
        return ;          
    }
    
    void
    ExchangeListClient::onError( OTError* const otError )
    {
        //
        // Display a message indicating the error type and description.
        //
        std::ostringstream error_message ;
        error_message << __FILE__ << ":" << __LINE__ << ": " 
                      << "ERROR: [" << otError->getRequestId()
                      << ":" << otError->getCode()  
                      << "] "
                      << otError->getDescription() 
                      << "."
                      << std::endl ;
        std::cerr << error_message.str() ;
        //
        // Set a flag indicating that processing has stopped due to an 
        // error. Unfortunately, one cannot throw an exception here because
		// of the way otfeed library is implemented; it catches all exceptions
		// through by Client::onError() and simply ignores them. This will 
		// hopefully be fixed in future.
        //
        this->mIsErrorEncountered = true ;
        return ;        
    }
}
